/*
 * java_strings.c
 *
 * This file is (C) 2010 by the developers of pidgin-java.
 *
 * Pidgin, Finch, and libpurple are owned by their respective developers:
 * see http://pidgin.im
 *
 * This file is part of pidgin-java.
 *
 * pidgin-java is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * pidgin-java is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with pidgin-java.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "internal.h"
#include "java_strings.h"

/*
 * Convert a NULL-terminated UTF-8 string into a modified UTF-8
 * NULL-terminated string. You must call g_free on the returned
 * string. If the conversion fails, returns NULL.
 */
gchar *
utf8z_to_modutf8(const gchar *s)
{
	const guint8 *p;
	GString *out;

	out = g_string_new(NULL);

	for (p = (guint8 *) s; '\0' != *p; p++) {
		if (0 == (*p & 0x80)) {
			g_string_append_c(out, *p);
		}
		else if (0xc0 == (*p & 0xe0)) {
			if (0x80 == (*(p + 1) & 0xc0)) {
				g_string_append_len(out, (const gchar *) p, 2);
				p++;
			}
			else {
				goto INVALID_INPUT;
			}
		}
		else if (0xe0 == (*p & 0xf0)) {
			if ((0x80 == (*(p + 1) & 0xc0)) &&
				(0x80 == (*(p + 2) & 0xc0))) {
				g_string_append_len(out, (const gchar *) p, 3);
				p += 2;
			}
			else {
				goto INVALID_INPUT;
			}
		}
		else if (0xf0 == (*p & 0xf8)) {
			if ((0x80 == (*(p + 1) & 0xc0)) &&
				(0x80 == (*(p + 2) & 0xc0)) &&
				(0x80 == (*(p + 3) & 0xc0))) {
				guint32 uval;

				uval =	((((*p       & 0x07) << 2) | ((*(p + 1) & 0x30) >> 4)) << 16) |
						((((*(p + 1) & 0x0f) << 4) | ((*(p + 2) & 0x3c) >> 2)) <<  8) |
						((((*(p + 2) & 0x03) << 6) | ((*(p + 3) & 0x3f)     ))      );
				if (uval <= 0x10ffff) {
					uval -= 0x10000;
					g_string_append_c(out, 0xed);
					g_string_append_c(out, 0xa0 | ((uval >> 16) & 0x0f));
					g_string_append_c(out, 0x80 | ((uval >> 10) & 0x3f));
					g_string_append_c(out, 0xed);
					g_string_append_c(out, 0xb0 | ((uval >>  6) & 0x0f));
					g_string_append_c(out, 0x80 | ( uval        & 0x3f));
				}
				p += 3;
			}
			else {
				goto INVALID_INPUT;
			}
		}
	}

	return g_string_free(out, FALSE);

INVALID_INPUT:
	g_string_free(out, TRUE);
	return NULL;
}
